#include "globalData.h"
#include <iostream>
#include <cstring>

using namespace std;

/* the following section implements bit shifting
   to operate on the BitBoard struct                                        */
   
BitBoard operator<<(const BitBoard & bb, int i)
{
    if (i < 0)
    {
        return bb >> (-i);
    }
    
    BitBoard result = bb;
    
    if (i > 0 && i < 50)
    {
        result.bottom = result.bottom << i;
        result.bottom = result.bottom | ((result.top & BitMaskHigh[i]) >> (50 - i));
        result.top = result.top << i;
    }
    else if (i >= 50)
    {
        result.bottom = result.top << (i - 50);
        result.top = 0;
    }
    
    result.top &= FIFTY_MASK;
    result.bottom &= FIFTY_MASK;
    
    return result;
}

BitBoard operator>>(const BitBoard & bb, int i)
{
    if (i < 0)
        return bb << (-i);
        
    BitBoard result = bb;

    if (i > 0 && i < 50)
    {
        result.top = (result.top & FIFTY_MASK) >> i;
        result.top = result.top | ((result.bottom & BitMaskLow[i]) << (50 - i));
        result.bottom = (result.bottom & FIFTY_MASK) >> i;
    }
    else if (i >= 50)
    {
        result.top = (FIFTY_MASK & result.bottom) >> (i - 50);
        result.bottom = 0;
    }

    return result;
}   

void printbitssimple(unsigned long long n) {
	unsigned long long i;
	i = ((long long) 1) <<(sizeof(n) * 8 - 1);

	while (i > 0) {
		if (n & i)
			cout << "1";
		else
			cout << "0";
		i >>= 1;
	}
}

static inline int LOG(U64 n)
{
    register U64 t, tt, ttt;
    
    int r;
    
    if (ttt = n >> 32)
    {
        if (tt = ttt >> 16)
        {
            r = (t = tt >> 8) ? 56 + LogTable256[t] : 48 + LogTable256[tt];
        }
        else 
        {
            r = (t = ttt >> 8) ? 40 + LogTable256[t] : 32 + LogTable256[ttt];
        }
    }
    else
    {
        if (tt = n >> 16)
        {
            r = (t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt];
        }
        else 
        {
            r = (t = n >> 8) ? 8 + LogTable256[t] : LogTable256[n];
        }
    }
    
    return r;
}

/* split the input BitBoard into the highest bit and the rest
   #in#  in         ... the BitBoard to split
   #out# out        ... the most significant bit 
   return           ... log of the highest bit                              */
int LOG_AND_SPLIT(BitBoard & in, BitBoard & out)
{
    assert((in.bottom & ~FIFTY_MASK) == 0);
    assert((in.top & ~FIFTY_MASK) == 0);
    
    int log;
    
    if (in.bottom & FIFTY_MASK)
    {
        out.top = 0;
        
        log = LOG(in.bottom);
        out.bottom = (1LL << log);
        in.bottom = in.bottom & ~out.bottom;
        
        log = log + 50;
    }
    else
    {
        out.bottom = 0;
        log = LOG(in.top);
        out.top = (1LL << log);
        
        in.top = in.top & ~out.top;
    }
    
    return log;
}



/* parse the board into a gameState structure
   #in#  b          ... the game board
   #out# blocked    ... output whether each square contains a block
   return           ... the parsed gameState structure                      */
gameState parseBoard(Board & b, BitBoard & blocked)
{
    gameState gs = {{0, 0}, {0, 0}};
    blocked.top = blocked.bottom = 0;
    
    for (int i = 0; i < 10; i++)
    {
        for (int j = 0; j < 10; j++)
        {
            int index = i * 10 + j;
            switch (b.getState(j, i))
            {
            case BLACK:
                SET_BIT(gs.black, index);
                break;
            case WHITE:
                SET_BIT(gs.white, index);
                break;
            case BLOCKED:
                SET_BIT(blocked, index);
                break;
            }
        }
    }

    return gs;
}


/* print the BitBoard
   #in#  bb         ... the gameState to print
   #in#  indent     ... number of spaces at beginning of each line          */
void printBitBoard(BitBoard bb, int indent)
{
    for (int i = 0; i < 50; i++)
    {
        if (i % 10 == 0)
        {
            cout << endl;
            for (int j = 0; j < indent-1; j++) 
                cout << " ";
        }
        if (bb.top & (1LL << i))
            cout << " " << 1;
        else
            cout << " " << 0;
    }
    for (int i = 0; i < 50; i++)
    {
        if (i % 10 == 0)
        {
            cout << endl;
            for (int j = 0; j < indent-1; j++) 
                cout << " ";
        }
        if (bb.bottom & (1LL << i))
            cout << " " << 1;
        else
            cout << " " << 0;
    }
    cout << endl;
}

/* get the set of of available moves for the current player
   #in#  gs         ... the current gameState
   #in#  blocked    ... true indicates each square is blocked
   #in#  player     ... the current player
   #out# moves      ... the list of valid moves
   return           ... number of available moves                           */
int getMoves(gameState gs, BitBoard blocked, BoardState player, int moves[], BitBoard effect[])
{
    int _count = 0;
    BitBoard mine, opponent, empty, temp;
    
    if (player == BLACK)
    {
        mine = gs.black;
        opponent = gs.white;
    }
    else
    {
        mine = gs.white;
        opponent = gs.black;
    }
    
    empty = ~(mine | opponent | blocked);
    
    BitBoard result = {0, 0};
    
    for (int i = 0; i < BRANCH_SIZE; i++)
    {
        effect[i].top = effect[i].bottom = 0;
    }
    
    for (int i = 0; i < MAX_DIRECTION; i++)
    {
        int length = 0;
        
        temp = (mine << DIRECTION_SHIFT[i]) & DIRECTION_MASK[i] & opponent;
        
        while (temp != 0)
        {
            length++;
            
            temp = (temp << DIRECTION_SHIFT[i]) & DIRECTION_MASK[i];
            
            BitBoard temp2 = temp & empty;
            
            while (temp2 != 0)
            {                
                // split into moves and gather information about how to flip
                BitBoard move_bb;
                int move_i = LOG_AND_SPLIT(temp2, move_bb);
                    
                for (int j = 1; j <= length; j++)
                {
                    effect[move_i] = effect[move_i] | (move_bb >> (DIRECTION_SHIFT[i] * j));
                }
                
                
                if ((move_bb & result) == 0)
                {
                    moves[_count] = move_i;
                    _count++;
                    result = result | move_bb;
                }
            }
            temp = temp & opponent;
        }
    }
    return _count;
}


/* verify m is in the list of moves
   #in#  m          ... the move to be checked
   #in#  moves      ... the list of all possible moves
   #in#  count      ... the total number of moves                           */
bool validMove(int m, int moves[], int count)
{
    for (int i = 0; i < count; i++)
    {
        if (m == moves[i])
            return true;
    }
    return false;
}
   



/* print the current board, use b for black, w for white, * for blocks
   #in#  gs         ... the current gameState
   #in#  blocked    ... true indicates each square is blocked
   #in#  indent     ... number of spaces at beginning of each line          */
void printGameState(gameState gs, BitBoard blocked, int indent)
{
    for (int _t = 0; _t < indent + 1; _t++)
        cout << " ";
            
    for (int i = 0; i < 10; i++)
    {
        cout << i << " ";
        
    }
    cout << endl;
    for (int i = 0; i < 10; i++)
    {
        cout << i;
        for (int _t = 0; _t < indent; _t++)
            cout << " ";
        for (int j = 0; j < 10; j++)
        {
            int index = i * 10 + j;
            if (GET_BIT(gs.black, index))
            {
                cout << "b ";
            }
            else if (GET_BIT(gs.white, index))
                cout << "w ";
            else if (GET_BIT(blocked, index))
                cout << "* ";
            else
                cout << ". ";
        }
        cout << endl;
    }

}

/* apply the player's move, flip bits and return new game state
   #in#  gs         ... the current gameState
   #in#  player     ... the moving player
   #in#  index      ... the move index
   #in#  flipbits   ... the calculated effect of the move                       
   return           ... the new gameState                                     */
gameState applyMove(const gameState & gs, BoardState player,
                    int index,    const BitBoard & flipbits)
{
    gameState result;
    
    result.black = gs.black ^ flipbits;
    result.white = gs.white ^ flipbits;
    
    if (player == BLACK)
    {
        SET_BIT(result.black, index);
    }
    else
    {
        SET_BIT(result.white, index);
    }
    
    return result;
}

/* parse the array notation of the board into bit board representation
   #in#  arr        ... array representation, each element is
                        {EMPTY, WHITE, BLACK, BLOCKED}
   #in#  color      ... the color to parse
   return           ... the bit board of the chosen color                     */
BitBoard ArrayToBitBoard(BoardState arr[][10], BoardState color)
{
    BitBoard r = {0, 0};
    for (int i = 0; i < 10; i++)
    {
        for (int j = 0; j < 10; j++)
        {
            if (arr[i][j] == color)
            {
                SET_BIT(r, i*10+j);
            }
        }
    }
    return r;
}
